package com.kgc.sbt.provider111;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.kgc.sbt.api.RushBApi;
import com.kgc.sbt.config.BuyLimitConfig;
import com.kgc.sbt.mapper.*;
import com.kgc.sbt.utils.RedisUtils;
import com.kgc.sbt.beans.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.jms.core.JmsMessagingTemplate;

import javax.jms.Destination;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created on 2021/8/19.
 * <p>
 * Author : Lcywings
 * <p>
 * Description :
 */
@Service
@Slf4j
public class RushBProvider implements RushBApi {

    @Autowired
    private BuyLimitConfig buyLimitConfig;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private AddressMapper addressMapper;

    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    @Autowired(required = false)
    private UserInformationMapper userInformationMapper;

    @Autowired(required = false)
    private ProductMapper productMapper;

    @Autowired(required = false)
    private RushbReminderMapper rushbReminderMapper;

    @Autowired(required = false)
    private UserLoginMapper userLoginMapper;

    @Autowired(required = false)
    private VipMapper vipMapper;

    @Override
    public boolean initProdStorkToRedis(Integer prodId) {
        // 创建库存查询对象
        StockExample stockExample = new StockExample();
        StockExample.Criteria criteria = stockExample.createCriteria();

        // 设置查询条件
        criteria.andProductIdEqualTo(prodId);

        // 查询库存
        Integer Count = stockMapper.selectByExample(stockExample).get(0).getCount();

        // 如果redis中已经初始化过商品库存数量，不需要重复操作，如果没有才需要初始化
        return redisUtils.get(buyLimitConfig.getStockNumPrefix() + prodId) == null
                && redisUtils.set(buyLimitConfig.getStockNumPrefix() + prodId, Count);
    }

    @Override
    public boolean lockBuyLimitUser(Integer userId, Integer prodId) {
        // 使用redis分布式锁，锁定当前商品的抢购用户，借助名称空间
        return redisUtils.lock(buyLimitConfig.getLockUerPrefix() + prodId + ":" + userId, 1, buyLimitConfig.getBuyLimitTime());

    }

    @Override
    public boolean checkProdStock(Integer prodId, Integer buyCount) {
        // 从redis获取商品库存数量
        Object prodStockNum = redisUtils.get(buyLimitConfig.getStockNumPrefix() + prodId);
        return prodStockNum != null && Integer.valueOf(prodStockNum.toString()) >= buyCount;
    }

    @Override
    public boolean isProdStockLocked(Integer prodId) {
        // 直接使用分布式锁，上锁，如果上锁失败，说明已经被锁定，如果上锁成功，没有被锁，可以进行抢购
        return !redisUtils.lock(buyLimitConfig.getLockProdStockPrefix() + prodId, null, buyLimitConfig.getLockProdStockTime());
    }

    @Override
    public void setTradeOrderTORedis(Integer userId, Integer prodId, String tradeOrderNo) {
        // 使用用户编号作为唯一标识（方便前端根用户编号判断订单是否生成成功）
        Map<String, Object> tradeOrderMap = new HashMap<>();
        tradeOrderMap.put("tradeOrderNo ", tradeOrderNo);
        // 订单状态为0，代表正在生成订单
        tradeOrderMap.put("tradeOrderStatus", 0);
        redisUtils.hmset(userId + "-" + prodId, tradeOrderMap, buyLimitConfig.getTradeOrderLimitTime());
    }

    @Override
    public int getCreatedTradeOrderStatus(Integer userId, Integer prodId) {
        // 判断订单是否超时（生成订单存入redis有个限制时间，超出时间自动删除），没有订单号，覆盖订单状态为超时取
        if (!redisUtils.hasKey(userId + "-" + prodId)) {
            redisUtils.hset(userId + "-" + prodId, "tradeOrderStatus", 3);
        }

        // 如果没有超时，获取订单状态（先判断redis中是否真的没有超时，防止上一步还没超时，到这一步超时）
        if (!ObjectUtils.isEmpty(redisUtils.hget(userId + "-" + prodId, "tradeOrderStatus"))) {
            return (int) redisUtils.hget(userId + "-" + prodId, "tradeOrderStatus");
        }

        // 返回订单正在生成标识,继续等待
        return 0;
    }

    @Override
    public void unlockBuyLimitUser(Integer userId, Integer prodId) {
        // 删除锁用户
        redisUtils.delLock(buyLimitConfig.getLockProdStockPrefix() + userId + ":" + prodId);
    }

    @Override
    public void unlockProdStock(Integer prodId) {
        // 删除锁库存
        redisUtils.delLock(buyLimitConfig.getLockProdStockPrefix() + prodId);
    }

    @Override
    public void subProdStockNum(Integer prodId, Integer buyCount) {
        // 抢购成功，减少redis中库存数量
        redisUtils.decr(buyLimitConfig.getStockNumPrefix() + prodId, buyCount);

        // 抢购成功，减少数据库中库存数量
        StockExample stockExample = new StockExample();
        StockExample.Criteria criteria = stockExample.createCriteria();
        criteria.andProductIdEqualTo(prodId);

        // 查出来再更新
        Stock stock = stockMapper.selectByExample(stockExample).get(0);
        stock.setCount(stock.getCount() - buyCount);
        stockMapper.updateByPrimaryKey(stock);
    }


    @Override
    public void createBuyLimitTradeOrder(Integer userId, Integer prodId, Integer buyCount, String tradeOrderNo) {
        log.info("****** 收到生成订单请求，用户编号:{}，商品编号:{}，交易订单号:{}， ******", userId, prodId, tradeOrderNo);

        // 收到下单请求，必须要做参数校验，用户信息获取，商品信息获取，创建订单实体对象等待
        if (ObjectUtils.isEmpty(userInformationMapper.selectByPrimaryKey(userId))) {
            log.error("用户不存在！");
        }
        if (ObjectUtils.isEmpty(productMapper.selectByPrimaryKey(prodId))) {
            log.error("商品不存在！");
        }

        log.info("****** 将生成订单请求发送到异步生成订单消息队列中 ******");

        // 创建消息队列
        Destination destination = new ActiveMQQueue("buy-limit-create-order-queue");

        // 创建消息对象
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("userId", userId.toString());
        paramMap.put("prodId", prodId.toString());
        paramMap.put("tradeOrderNo", tradeOrderNo);
        OrderDetail orderDetail = this.createRushBOrder(userId, prodId, buyCount, tradeOrderNo);
        OrderMain orderMain = this.createRushBOrderMain(userId, prodId, buyCount, tradeOrderNo,
                Double.valueOf(orderDetail.getTotalPrice()) * buyCount, Double.valueOf(orderDetail.getFavorableAverage()));

        OrderAll orderAll = new OrderAll();
        orderAll.setOrderDetail(orderDetail);
        orderAll.setOrderMain(orderMain);
        orderAll.setParamMap(paramMap);

        log.info("向消息队列发送信息");
        // 发送消息队列,注意：使用高可用的消息队列-主从或者集群，防止队列不可用或者消息丢失
        jmsMessagingTemplate.convertAndSend(destination, JSON.toJSONString(orderAll));
    }

    public OrderDetail createRushBOrder(Integer userId, Integer prodId, Integer buyCount, String tradeOrderNo) {
        // 声明订单详情类用于返回
        OrderDetail orderDetail = new OrderDetail();

        // 从数据库获取商品详情
        Product product = productMapper.selectByPrimaryKey(prodId);

        // 生成订单
        orderDetail.setOrderNum(tradeOrderNo);
        orderDetail.setProductId(prodId);
        orderDetail.setProductName(product.getProductName());
        orderDetail.setProductQuantity(buyCount);
        orderDetail.setProductPrice(product.getPrice().toString());
        orderDetail.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(buyCount)).toString());
        orderDetail.setFavorableAverage("0");

        log.info("生成详细订单数据：{}", orderDetail);

        return orderDetail;
    }

    public OrderMain createRushBOrderMain(Integer userId, Integer prodId, Integer buyCount,
                                          String tradeOrderNo, Double totalPrice,
                                          Double totalPriceByDisCount) {
        // 查个用户地址对象出来填数据
        AddressExample addressExample = new AddressExample();
        AddressExample.Criteria criteria = addressExample.createCriteria();
        criteria.andUserIdEqualTo(userId);
        Address address = new Address();
        try {
            address = addressMapper.selectByExample(addressExample).get(0);
        } catch (Exception e) {
            log.error("用户没有初始地址");
            e.printStackTrace();
        }

        // 声明主订单类用于返回
        OrderMain orderMain = new OrderMain();
        orderMain.setOrderNum(tradeOrderNo);
        orderMain.setUserId(userId);

        orderMain.setReceiverName(address.getReceiverName());
        orderMain.setReceiverPhone(address.getReceiverMobile());
        orderMain.setReceiverProvince(address.getReceiverProvince());
        orderMain.setReceiverCity(address.getReceiverCity());
        orderMain.setReceiverDistrict(address.getReceiverDistrict());
        orderMain.setReceiverAddress(address.getReceiverAddress());
        orderMain.setPayType("微信");
        orderMain.setOrderAmount(String.valueOf(totalPrice));

        orderMain.setCouponsAmount("0");

        if (vipMapper.selectVipStatusByUserId(userId) == 1) {
            orderMain.setVipAmount(String.valueOf((double) Math.round(totalPrice * 0.1 * 100) / 100));
        } else {
            orderMain.setVipAmount("0");
        }
        orderMain.setFreight("包邮");
        orderMain.setPayAmount(String.valueOf(totalPriceByDisCount));
        orderMain.setLogisticsId(1);
        orderMain.setOrderTime(new Date());
        orderMain.setPayTime(new Date());
        orderMain.setOrderStatus(2);
        // 一元钱十积分
        orderMain.setOrderIntegral(String.valueOf(Math.floor(totalPriceByDisCount * 10)));

        log.info("生成主订单数据：{}", orderMain);

        return orderMain;
    }

    @Override
    public Boolean appointment(Integer userId, Integer prodId) {
        // 查表看该用户是否已经已经预约了该商品
        RushbReminderExample rushbReminderExample = new RushbReminderExample();
        rushbReminderExample.createCriteria().andProductIdEqualTo(prodId);
        rushbReminderExample.createCriteria().andUserIdEqualTo(userId);

        // 查用户手机
        String phone = userLoginMapper.selectByPrimaryKey(userId).getUserPhone();
        log.info("查用户手机:{}", phone);

        // 判断是否已经预约
        if (!ObjectUtils.isEmpty(rushbReminderMapper.selectByExample(rushbReminderExample))) {
            // 已经预约了，要取消预约
            rushbReminderMapper.deleteByExample(rushbReminderExample);
            redisUtils.del("appointment:" + userId + ":" + prodId + ":" + phone);
            log.info("已经预约");
            return false;
        }
        log.info("暂无预约");

        // 插表
        log.info("可以预约，向表中增加预约信息");
        RushbReminder rushbReminder = new RushbReminder();
        rushbReminder.setProductId(prodId);
        rushbReminder.setUserId(userId);
        rushbReminder.setUserPhone(phone);
        rushbReminderMapper.insert(rushbReminder);


        // 计算提醒时间
        Product p = productMapper.selectByPrimaryKey(prodId);
        log.info("p:{}", p);
        Date d = p.getSailStart();
        log.info("d:{}", d);
        Long startTime = d.getTime();
        log.info("startTime:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTime));
        Long nowTime = new Date().getTime();
        log.info("nowTime:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(nowTime));
        Long countDown = (startTime - nowTime) / 1000;
        log.info("倒计时：{}", countDown);

        // 设置过期redis用于预约
        log.info("可以预约，向redis设置过期时间");
        redisUtils.set("appointment:" + userId + ":" + phone + ":" + productMapper.selectByPrimaryKey(prodId).getProductName(), 1, countDown);

        return true;
    }

    @Override
    public Boolean isM2Product(Integer prodId) {
        if (productMapper.selectByPrimaryKey(prodId).getFirstId().equals("M2")) {
            log.info("是抢购商品");
            return true;
        }
        log.info("不是抢购商品");
        return false;
    }

    @Override
    public Boolean isM2ProductBuyTime(Integer prodId) {
        if (productMapper.selectByPrimaryKey(prodId).getSailStart().getTime() > new Date().getTime()) {
            log.info("抢购商品可以抢购，现在：{}，开始时间：{}",
                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), productMapper.selectByPrimaryKey(prodId).getSailStart());
            return true;
        }
        log.info("抢购商品超出抢购时间，现在：{}，开始时间：{}",
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), productMapper.selectByPrimaryKey(prodId).getSailStart());
        return false;
    }

    @Override
    public Boolean isM2ProductBuyTime2(Integer prodId) {
        Long startTime = productMapper.selectByPrimaryKey(prodId).getSailStart().getTime();
        Long endTime = productMapper.selectByPrimaryKey(prodId).getSailEnd().getTime();
        Long nowTime = new Date().getTime();
        if (startTime < nowTime && endTime > nowTime) {
            log.info("抢购商品可以抢购，现在：{}，开始时间：{},结束时间：{}", nowTime, startTime, endTime);
            return true;
        }
        log.info("抢购商品可以抢购，现在：{}，开始时间：{},结束时间：{}", nowTime, startTime, endTime);
        return false;
    }
}
